#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string>
using namespace std;

#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]#"

//shell自定义的全局变量
//1.命令行参数表
#define MAXARGC 128
char* g_argv[MAXARGC];
int g_argc = 0;

//2.环境变量表
#define MAXENVS 100
char* g_env[MAXENVS];
int g_envs = 0;

//for test
char cwd[1024];
char cwdenv[1024];

//last exit code
int lastcode = 0;

const char* GetUserName()
{
    const char* name = getenv("USER");
    return name == NULL ? "None" : name; 
}

const char* GetHostName()
{
    const char* hostname = getenv("HOSTNAME");
    return hostname == NULL ? "None" : hostname;
}

const char* GetPwd()
{
    //const char* pwd = getenv("PWD");
    const char* pwd = getcwd(cwd,sizeof(cwd));
    if(pwd != NULL)
    {
      snprintf(cwdenv,sizeof(cwdenv),"PWD=%s",pwd);
      putenv(cwdenv);
    }
    return pwd == NULL ? "None" : pwd;
}

const char* GetHome()
{
    const char* home = getenv("HOME");
    return home == NULL ? "" : home;
}

std::string DirName(const char* pwd)
{
#define SLASH "/"
    std::string dir = pwd;
    if(dir == SLASH)
    {
      return SLASH;
    }
    auto pos = dir.rfind(SLASH);
    if(pos == std::string::npos)
    {
      return "BUG";
    }
    return dir.substr(pos + 1);
}

void MakeCommandLine(char cmd_prompt[],int size)
{
    //snprintf(cmd_prompt,size,"[%s@%s %s]#",GetUserName(),GetHostName(),GetPwd());
    //snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),GetPwd());
    snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),DirName(GetPwd()).c_str());
}


void PrintCommandPrompt()
{
    char prompt[COMMAND_SIZE];
    MakeCommandLine(prompt,sizeof(prompt));
    printf("%s",prompt);
    fflush(stdout);
}

bool GetCommandLine(char* out,int size)
{ 
    char* c = fgets(out, size,stdin);
    if(c == nullptr)
    {
       return false; 
    }
    //去掉\n
    c[strlen(out) -1] = 0;
    if(strlen(c) == 0)
    {
       return false;
    }
    return true;
}

//命令行分析 "ls -a -l" -> "ls" "-a" "-l"
bool CommandParse(char* commandline)
{
#define SEP " "
    g_argc = 0;
    g_argv[g_argc++] = strtok(commandline,SEP);
    while((bool)(g_argv[g_argc++] = strtok(nullptr,SEP)));
    //会多一个减掉
    g_argc--;
    return g_argc > 0 ? true : false;
}

void InitEnv()
{
    extern char** environ;
    memset(g_env,0,sizeof(g_env));
    g_envs = 0;
    
    //配置文件
    //获取环境变量
    for(int i = 0;environ[i];i++)
    {
        //申请空间
        g_env[i] = (char*)malloc(strlen(environ[i]) + 1);
        strcpy(g_env[i],environ[i]);
        g_envs++;
    }
    //for test
    g_env[g_envs++] = (char*)"MIHAYOU=666"; 
    g_env[g_envs] = NULL; 

    //导成环境变量
    for(int i = 0;g_env[i];i++)
    {
       putenv(g_env[i]);
    }
    environ = g_env;
}

bool Cd()
{
  if(g_argc == 1)
  {
    string home = GetHome();
    if(home.empty())
    {
      return true;
    }
    chdir(home.c_str());
  }
  else
  {
    string where = g_argv[1];
    chdir(where.c_str());
  }
  return true;
}

void Echo()
{
    if(g_argc == 2)
    {
        //echo "hello"
        //echo $?
        //echo $PATH
        string opt = g_argv[1];
        if(opt == "$?")
        {
            cout << lastcode << endl;
        }
        else if(opt[0] == '$')
        {
            string env_name = opt.substr(1);
            const char* env_value = getenv(env_name.c_str());
            if(env_value)
            {
                cout << env_value << endl;
            }

        }
        else
        {
            cout << opt << endl;
        }
    }
}

bool CheckAndExecBuiltin()
{
    string cmd = g_argv[0];
    if(cmd  == "cd")
    {
      Cd();
      return true;
    }
    else if(cmd == "echo")
    {
      Echo();
      return true;
    }
    return false;
}

void PrintArgv()
{
    for(int i = 0;g_argv[i];i++)
    {
      printf("argv[%d]->%s\n", i,g_argv[i]);
    }
    printf("size = %d\n", g_argc);
}

void clear()
{
    for(int i = 0;g_env[i];i++)
    {
      free(g_env[i]);
      g_env[i] = NULL;
    }
}

int Execute()
{
    pid_t id = fork();
    if(id == 0)
    {
        //子进程执行
        execvp(g_argv[0],g_argv);
        //程序替换后面的程序就不执行了
        exit(1);
    }
    
    //父进程等待
    int status = 0;
    pid_t rid = waitpid(id,&status,0);
    ////取消报警
    //(void)rid;
    if(rid > 0)
    {
        lastcode = WEXITSTATUS(status);
    }
    return 0;
}

int main()
{
    //shell启动的时候从系统获得环境变量
    //我们的环境变量信息应该统一从父shell取
    InitEnv();

    while(1)
    {
      
      //1.打印提示信息
      //printf("[%s@%s %s]#", GetUserName(),GetHostName(),GetPwd());
      PrintCommandPrompt();

      //2.获取命令行提示符
      char commandline[COMMAND_SIZE];
      if(!GetCommandLine(commandline,sizeof(commandline)))
      {
        continue;
      }

      //char* c = fgets(commandline, COMMAND_SIZE,stdin);
      //if(c == nullptr)
      //{
      //  continue; 
      //}
      ////去掉\n
      //c[strlen(commandline) -1] = 0;
      //if(strlen(c) == 0)
      //{
      //  continue;
      //}
      //打印命令行提示符
      //printf("%s\n",c);
      //printf("%s\n",commandline);
      //for(int i = 0;i < strlen(c);i++)
      //{
      //  cout << c[i];
      //}
      
      //3.命令行分析
      if(!CommandParse(commandline))
      {
        continue;
      }
      //PrintArgv();
      
      //4.检测是否是内键命令
      if(CheckAndExecBuiltin())
      {
        continue;
      }

      //5.执行命令
      Execute();
    }

    //清理工作
    clear();
    return 0;
}
